package main

import (
	"context"
	"errors"
	"flag"
	"fmt"
	"github.com/gogo/protobuf/proto"
	"github.com/spf13/viper"
	"github.com/topfreegames/pitaya"
	"github.com/topfreegames/pitaya/acceptor"
	"github.com/topfreegames/pitaya/cluster"
	"github.com/topfreegames/pitaya/component"
	"github.com/topfreegames/pitaya/config"
	"github.com/topfreegames/pitaya/groups"
	"github.com/topfreegames/pitaya/logger"
	"github.com/topfreegames/pitaya/route"
	"github.com/topfreegames/pitaya/serialize/protobuf"
	"math/rand"
	"pitaya_new_chatroom/consts"
	"pitaya_new_chatroom/i18n"
	"pitaya_new_chatroom/manager/global"
	"pitaya_new_chatroom/manager/userSession"
	"pitaya_new_chatroom/module"
	"pitaya_new_chatroom/protos"
	"pitaya_new_chatroom/services"
	"reflect"
	"strings"
	"time"
)


type MyError struct {
	When time.Time
	What string
}

func (e *MyError) Error() string {
	return fmt.Sprintf("at %v, %s",
		e.When, e.What)
}



func main(){

	fmt.Println("test pitaya 1.1.7")

	/** 以下参数通过 启动参数传进来*/

	port:= flag.Int("port",3251 ,"进程监听的端口")
	serverType:= flag.String("type","chatroom","服务器进程type")
	isFrontend:=flag.Bool("frontend",true ,"服务器节点是否作为前端使用")
	env:= flag.String("env","development","当前环境")


	// 监控进程内状态
	prometheusPort := flag.Int("ptport",9090 ,"普罗米修斯上报端口")
	// 解析启动参数
	flag.Parse()
	// 如果 进程退出 那么 执行一次 pitaya shutdown 方法
	defer pitaya.Shutdown()
	/**
		设置一下config 相关
		*xxx 因为flag.(type)返回的是指针类型 所以要用*xxx 拿到 值

		configApp 返回一个 viper.Viper的指针
	*/

	conf := configApp(*serverType,*env, *prometheusPort)

	//挂载一下组信息 这里是使用Etcd来存放组信息 可以使用进程内存
	groupInstance ,err:= groups.NewEtcdGroupService(config.NewConfig(conf),nil)
	if err !=nil{
		//fmt.Printf("create etcd group error,%v",err)
	}
	// 调用一次 注册到pitaya上
	pitaya.InitGroups(groupInstance)

	global.G.UserSession = &userSession.MemoryUserSession{}



	// 设置序列化方式 位 protobuf （这里不是通信协议 而是 对数据的 序列 反序列）
	ser := protobuf.NewSerializer()
	pitaya.SetSerializer(ser)
	//判断进程类型 前端还是后端 （前端是只和客户端保持连接的进程）通过传进来的isFrontend判断

	if *isFrontend {
		configureFrontend(*port)
	} else {
		configureBackend()
	}
	// 使用集群模式 前端和后端的通信 默认使用 Nats
	pitaya.Configure(*isFrontend, *serverType, pitaya.Cluster, map[string]string{
		"serverStatus":consts.Const["SERVICE_RUNNING"].(string),
	},conf)

	// 注册一下 日志轮转服务
	lr:=module.NewLoggerRotate()
	pitaya.RegisterModule(lr,"loggerRotate")

	//启动 pitaya服务
	pitaya.Start()

}

// 前端进程 注册逻辑
func configureFrontend(port int){
	// 声明连接类型
	tcp := acceptor.NewTCPAcceptor(fmt.Sprintf(":%d", port))

	pitaya.Register(&services.Connector{},
		component.WithName("connector"),
		component.WithNameFunc(strings.ToLower),
	)

	pitaya.Register(&services.FrontendOps{},
		component.WithName("frontendops"),
		component.WithNameFunc(strings.ToLower),
	)

	pitaya.RegisterRemote(&services.FrontendOps{},
		component.WithName("frontendopsremote"),
		component.WithNameFunc(strings.ToLower),
	)

	pitaya.RegisterRemote(&services.ConnectorRemote{},
		component.WithName("connectorremote"),
		component.WithNameFunc(strings.ToLower),
	)

	routerCallback:=func(
		ctx context.Context,
		route *route.Route,
		payload []byte,
		servers map[string]*cluster.Server,
	)(*cluster.Server,error){
		// 修改服务器状态 不受维护限制
		if route.Method == "setserverstatus"{
			reqData:= protos.ServerStatus{}
			proto.Unmarshal(payload,&reqData)
			if reqData.ServerId != ""{
				if serverInfo,isExists:= servers[reqData.ServerId];isExists{
					return serverInfo,nil
				}else{
					return nil ,pitaya.Error(errors.New("服务器节点不存在"),"SERVER_NODE_NO_EXISTS")
				}
			}else{
				return nil ,pitaya.Error(errors.New("服务器节点不存在"),"SERVER_NODE_NO_EXISTS")
			}
		}

		// 过滤 维护的服务器
		srvList := make([]*cluster.Server, 0)
		for _, v := range servers {

			if status,isOk:=services.ServerStatusList[v.ID];isOk{
				if status == consts.Const["SERVICE_RUNNING"]{
					srvList = append(srvList, v)
				}
			}else{
				srvList = append(srvList, v)
			}
		}
		//如果没有可用的服务器 那么提示客户端
		if len(srvList) == 0{
			return nil,pitaya.Error(errors.New(i18n.I18n.GetText(ctx,"没有可用服务器，服务器维护中")),"SYS_MAINTAIN")
		}

		seed := rand.NewSource(time.Now().Unix())
		rnd := rand.New(seed)

		s := pitaya.GetSessionFromCtx(ctx)
		if s.Get("backendServerId")==nil{
			return nil,pitaya.Error(errors.New(i18n.I18n.GetText(ctx,"请先登录")),"NO_LOGIN")
		}

		backendServerId:=s.Get("backendServerId").(string)

		if backendServerId != "" {

			if server,ok:=servers[backendServerId];ok {

				logger.Log.Debugf("通知指定服务器：%v",backendServerId)
				return server, nil
			}

		}

		return srvList[rnd.Intn(len(srvList))],nil
	}
	err:=pitaya.AddRoute("room",routerCallback)


	if err !=nil{
		fmt.Printf("error adding route %s\n", err.Error())
	}

	pitaya.AddAcceptor(tcp)

	//开个端口给ops运维用  端口5555

	ws:=acceptor.NewWSAcceptor("127.0.0.1:5555")

	pitaya.AddAcceptor(ws)
}

// 后端进程  注册逻辑
func configureBackend(){
	backend := &services.Backend{}
	pitaya.Register(backend, component.WithName("backend"), component.WithNameFunc(strings.ToLower))
	pitaya.RegisterRemote(backend, component.WithName("backend"), component.WithNameFunc(strings.ToLower))

	pitaya.Register(&services.Ops{},
		component.WithName("ops"),
		component.WithNameFunc(strings.ToLower),
	)

	pitaya.BeforeHandler(func(ctx context.Context, in interface{})(context.Context ,interface{}, error){
		server := pitaya.GetServer()
		dataType:=reflect.TypeOf(in)

		if dataType !=nil && dataType.String() == "*protos.ServerStatus"{
			return ctx, in ,nil
		}
		if val,ok:=server.Metadata["serverStatus"];ok{
			if val == consts.Const["SERVICE_STOP"].(string){
				return ctx, nil, pitaya.Error(errors.New(i18n.I18n.GetText(ctx,"服务器维护中")),"SYS_MAINTAIN")
			}
		}

		return ctx, in ,nil
	})
}

func configApp(serverType,env string ,prometheusPort int) *viper.Viper{
	conf := viper.New()
	//设置文件名 之后后面搜索配置 就会使用configName  默认是config
	conf.SetConfigName("config")
	conf.AddConfigPath("configs")
	//上面增加了配置文件的根目录，ReadInConfig会把配置文件内容 读到 v.config上 默认的配置是在 default上

	conf.ReadInConfig()

	// 处理程序接收并在本地处理的消息的缓冲区大小(意思就是本地消息如果超过15条，后来的消息等待)
	//make(chan unhandledMessage, localProcessBufferSize)
	conf.SetDefault("pitaya.buffer.handler.localprocess", 15)
	// 开启prometheus
	conf.SetDefault("pitaya.metrics.prometheus.enabled", true)
	conf.SetDefault("pitaya.metrics.prometheus.port",prometheusPort)

	// 客户端连接的Keepalive心跳间隔
	conf.Set("pitaya.heartbeat.interval", "2s")

	//每个代理收到的客户端消息的缓冲区大小 (通道)
	conf.Set("pitaya.buffer.agent.messages", 32)
	// 客户端和服务器之间的消息是否应该压缩
	conf.Set("pitaya.handler.messages.compression", false)

	// 日志轮转
	conf.Set("chatroom.env",env)
	conf.Set("chatroom.loggerRotate.path","logs/error.log")
	conf.Set("chatroom.loggerRotate.compress",false)
	conf.Set("chatroom.loggerRotate.maxbackups",0)
	conf.Set("chatroom.loggerRotate.maxage",0)
	conf.Set("chatroom.loggerRotate.maxsize",100)
	conf.Set("chatroom.loggerRotate.source",fmt.Sprintf("chatroom-%s:%s",serverType,pitaya.GetServerID()))


	return conf

}
